﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

using Toom.CellularAutomata.Model;
using Toom.CellularAutomata.Parser;
using Toom.CellularAutomata.Debug;
using Toom.CellularAutomata.Common;


namespace Toom.CellularAutomata.GUI
{
	public partial class FormCAMAIN : Form
	{

		#region class FormCAMAIN: Ctor
		public FormCAMAIN()
		{
			InitializeComponent();

			// set window title
			this.Text = Application.ProductName + " " + Application.ProductVersion;

			// init settings (default colors, etc)
			CASettings.Setup();

			// prepare FormField
			PrepareField();

			// prepare runner object for background work
			runner = new CARunner(this);

			// create performance tracker
			perf = new CAPerformanceTracker();
			perf.CreateNewTracker( "calc" );
			perf.CreateNewTracker( "draw" );
			perf.CreateNewTracker( "drawTitle" );
		}

		#endregion


		#region class FormCAMAIN: Data

		public CAField field = null;
		public FormField formField = null;

		public CARunner runner = null;
		public CAPerformanceTracker perf = null;

		public FormDebug formDebug = null;
		public FormDebugChunks formDebugChunks = null;

		#endregion



		#region class FormCAMAIN: Load and Unload
		private void FormCAMAIN_Load(object sender, EventArgs e)
		{
			// initial settings
			labelRunStatus.Text = "idle";
			numericWorkerSleep.Value = (decimal)runner.workerSleepInSec;
			//comboPenFlavor.SelectedIndex = 0;
			checkBoxDrawEnabled.Checked = CASettings.drawEnabled;

			EnableItemsNeedingField( false );

			// prepare game types
			comboBoxWhichGame.Items.Clear();
			List<CAGameType> list = CARuleStatic.GetAllGameTypes();
			foreach ( CAGameType gt in list )
				comboBoxWhichGame.Items.Add( gt );
			comboBoxWhichGame.SelectedIndex = 0;

			// read current size
			linkUseAvailableSize_LinkClicked(null, null);

		}

		private void FormCAMAIN_FormClosing( object sender, FormClosingEventArgs e )
		{
			// stop the running thread
			if ( runner.workerRunning && runner.thread.IsAlive )
			{
				runner.workerStopRequested = true;
				runner.thread.Join();
			}

			bool yesExit = ConfirmClosing();

			if ( !yesExit )
				e.Cancel = true;
		}

		#endregion


		#region Init Buttons

		private void buttonInitEmpty_Click( object sender, EventArgs e )
		{
			if ( !CreateField() )
				return;
			field.InitEmpty();
			DisplayField();

			EnableItemsNeedingField();
		}

		private void buttonInitRandom_Click(object sender, EventArgs e)
		{
			if ( !ParseUserSettings() )
				return;

			if (!CreateField())
				return;
			field.InitRandom( CASettings.valMin, CASettings.valMax );
			DisplayField();

			EnableItemsNeedingField();
		}
		private void buttonInitBar_Click(object sender, EventArgs e)
		{
			if ( !ParseUserSettings() )
				return;

			if ( !CreateField() )
				return;
			field.InitBar( CASettings.valMin, CASettings.valMax );
			DisplayField();

			EnableItemsNeedingField();
		}

		private void buttonInitDiamond_Click(object sender, EventArgs e)
		{
			if ( !ParseUserSettings() )
				return;

			if (!CreateField())
				return;

			field.InitDiamond( CASettings.valMin, CASettings.valMax );
			DisplayField();

			EnableItemsNeedingField();
		}

		private void buttonInitCircle_Click(object sender, EventArgs e)
		{
			if ( !ParseUserSettings() )
				return;

			if (!CreateField())
				return;
			field.InitCircle( CASettings.valMin, CASettings.valMax );
			DisplayField();

			EnableItemsNeedingField();
		}

		private void buttonVertStripes_Click(object sender, EventArgs e)
		{
			if ( !ParseUserSettings() )
				return;

			if (!CreateField())
				return;
			field.InitFillWithVerticalStripes( CASettings.valMin, CASettings.valMax );
			DisplayField();

			EnableItemsNeedingField();

		}

		#endregion


		private void linkUseAvailableSize_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			//int xMax = formField.glField.ClientSize.Width;
			//int yMax = formField.glField.ClientSize.Height;

			int xMax = formField.panelField.ClientSize.Width;
			int yMax = formField.panelField.ClientSize.Height; 
			textboxFieldSizeX.Text = xMax.ToString();
			textboxFieldSizeY.Text = yMax.ToString();
		}

		private void linkLabelIsAllZeros_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			bool allZeros = field.IsAllZero();
			MessageBox.Show(this, "field.IsAllZeros() returns " + allZeros);
		}




		#region Display Field

		public void DisplayField()
		{
			if ( field == null )
				return;

			formField.DisplayFieldAsImage(null);
		}




		#endregion


		private void linkRefreshField_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			DisplayField();

		}


		#region Buttons for Transformation (once and run)

		private void buttonTransform_Click( object sender, EventArgs e )
		{
			if ( runner.workerRunning )
			{
				ShowErrorDialog( "Transformations are running already" );
				return;
			}

			CATimer timer = new CATimer();

			CAGameType game = DetermineGameType();


			if ( game == CAGameType.CUSTOM )
			{

				try
				{
					runner.customRule = ParseCustomRule( textBoxUserRule.Text );
				}
				catch ( Exception ex )
				{
					ShowErrorDialog( ex.Message );
					return;
				}

				field.DoTransform( runner.customRule, CASettings.Optimization );
			}
			else
				field.DoTransform( game, CASettings.Optimization );

			double timeDiffS1 = timer.GetSAndReset();

			DisplayField();

			double timeDiffS2 = timer.GetSAndReset();

			if ( !runner.workerRunning )
				DebugPrintLine(
					CADebugger.DebugMessageSource.Perf,
					"Transform took " + timeDiffS1.ToString( "##0.000" ) +
					" Display took " + timeDiffS2.ToString( "##0.000" ) );

			perf.UpdateTracker( "calc", timeDiffS1 );

			if ( field.IsAllZero() )
				MessageBox.Show( this, "Field is all zeros" );
		}


		private void buttonTransRun_Click(object sender, EventArgs e)
		{

			if (runner.workerRunning)
			{
				labelRunStatus.Text = "Already running!";
				return;
			}


			// read sleep value
			try
			{
				runner.workerSleepInSec = (double)numericWorkerSleep.Value;
			}
			catch (Exception)
			{
				MessageBox.Show(this, "Invalid real number for sleep");
				return;
			}

	
			runner.game = DetermineGameType();

			try
			{
				if (runner.game == CAGameType.CUSTOM)
					runner.customRule = ParseCustomRule(textBoxUserRule.Text);
			}
			catch ( Exception ex)
			{
				ShowErrorDialog(ex.Message);
				return;
			}


			runner.thread = new Thread( runner.RunUpdatesToField );

			try
			{
				formField.glField.Context.MakeCurrent( null );
			}
			catch ( OpenTK.Graphics.GraphicsContextException gcex )
			{
				CADebugger.DebugPrintLine( CADebugger.DebugMessageSource.Painting, gcex.Message );
			}


			runner.thread.Start();

			runner.workerRunning = true;


			// diable panel for field reset
			EnableItemsThatResetField( false );
			
			runner.workerSavedBGColor = buttonTransStop.BackColor;
			runner.workerSavedFGColor = buttonTransStop.ForeColor;
			buttonTransStop.BackColor = Color.Red;
			buttonTransStop.ForeColor = Color.Blue;

			buttonTransOnce.Enabled = false;
			labelRunStatus.Text = "running";
		}

		private void buttonTransStop_Click(object sender, EventArgs e)
		{
			if ( !runner.workerRunning )
			{
				labelRunStatus.Text = "Already stopped!";
				return;
			}

			labelRunStatus.Text = "stopping...";

			runner.workerStopRequested = true;
			//runner.workerRunning = false;
			runner.thread.Join();

			//buttonTransRun.Text = runner.workerSavedButtonTitle;
			buttonTransStop.BackColor = runner.workerSavedBGColor;
			buttonTransStop.ForeColor = runner.workerSavedFGColor;

			buttonTransOnce.Enabled = true;
			//comboPenFlavor.Enabled = true;
			//checkBoxDrawEnabled.Enabled = true;

			labelRunStatus.Text = "idle";

			formField.glField.MakeCurrent();
			EnableItemsThatResetField( true );

		}


		private void checkBoxDrawEnabled_CheckedChanged( object sender, EventArgs e )
		{
			bool newState = checkBoxDrawEnabled.Checked;
			CASettings.drawEnabled = newState;
			checkBoxDrawLines.Enabled = newState;
		}

		private void checkBoxDrawLines_CheckedChanged( object sender, EventArgs e )
		{
			CASettings.drawLines = checkBoxDrawLines.Checked;
		}
		private void comboPenFlavor_SelectedIndexChanged( object sender, EventArgs e )
		{
			CASettings.selectedColor = comboPenFlavor.SelectedIndex;
		}



		#endregion


		#region Helper Functions for GameType and Custom Rules


		private void PrepareField()
		{
			formField = new FormField( this );

			formField.TopLevel = false;
			formField.Dock = DockStyle.Fill;
			formField.FormBorderStyle = FormBorderStyle.None;
			this.panelFieldHolder.Controls.Add( formField );
			formField.Show();
		}


		private bool ConfirmClosing()
		{
			DialogResult result = MessageBox.Show( this, "Close the application?", "Conform Exit", MessageBoxButtons.OKCancel );
			if ( result == DialogResult.OK )
				return true;
			else
				return false;
		}

		private bool ParseUserSettings()
		{
			bool allOk = true;

			allOk &= ParseOneUserSetting( textBoxAutomataValMin, out CASettings.valMin, "Problem with Min value" );
			allOk &= ParseOneUserSetting( textBoxAutomataValMax, out CASettings.valMax, "Problem with Max value" );

			if ( CASettings.valMin < 0 || CASettings.valMax < 0 )
			{
				ShowErrorDialog( "min and max should be >0" );
				allOk=false;
			}

			if ( CASettings.valMin > CASettings.valMax )
			{
				ShowErrorDialog( "min must be <= max" );
				allOk = false;
			}




			allOk &= ParseOneUserSetting( textboxFieldSizeX, out CASettings.fieldSizeX, "Problem with Field X" );
			allOk &= ParseOneUserSetting( textboxFieldSizeY, out CASettings.fieldSizeY, "Problem with Field Y" );

			if ( CASettings.fieldSizeX < 1 || CASettings.fieldSizeY < 1 )
			{
				ShowErrorDialog( "field size x and y must be 1 or greater" );
				allOk = false;
			}

			return allOk;
		}

		private bool ParseOneUserSetting( TextBox textbox, out int value, string messageIfProblem )
		{
			try
			{
				value = int.Parse( textbox.Text );
				return true;
			}
			catch ( Exception )
			{
				MessageBox.Show( this, messageIfProblem );
				textbox.Focus();
			}
			value = int.MinValue;	// otherwise wont compile
			return false;
		}


		private bool CreateField()
		{
			this.field = new CAField( CASettings.fieldSizeX, CASettings.fieldSizeY, CASettings.valMin, CASettings.valMax );
			formField.SetField( field );

			// since we now know minVal and MaxVal, we need to create current color palette
			SetupColors();

			return true;	// success
		}

		private void SetupColors()
		{
			CASettings.SetupColors( CASettings.valMax );

			comboPenFlavor.Items.Clear();
			for ( int color=CASettings.valMin ; color<=CASettings.valMax ; color++ )
			{
				int idx;
				idx = comboPenFlavor.Items.Add( "" + color + " " + CASettings.colors[color] );
				//comboPenFlavor.Items[idx]
			}

			// select first color
			comboPenFlavor.SelectedIndex = 0;
		}

		private CAGameType DetermineGameType()
		{
			if (radioButtonRuleCanned.Checked)
			{
				// can further reduce code here by just returning the cast object
				if ( (CAGameType)comboBoxWhichGame.SelectedItem == CAGameType.CONWAY )
					return CAGameType.CONWAY;
				if ( (CAGameType)comboBoxWhichGame.SelectedItem == CAGameType.NEC_MAX )
					return CAGameType.NEC_MAX;
				if ( (CAGameType)comboBoxWhichGame.SelectedItem == CAGameType.NEC_MIN )
					return CAGameType.NEC_MIN;
				if ( (CAGameType)comboBoxWhichGame.SelectedItem == CAGameType.NEC_MEDIAN )
					return CAGameType.NEC_MEDIAN;
				if ( (CAGameType)comboBoxWhichGame.SelectedItem == CAGameType.SWAP_ZEROS_AND_TWOS )
					return CAGameType.SWAP_ZEROS_AND_TWOS;
				return CAGameType.NONE;
			}
			else if (radioButtonRuleCustom.Checked)
				return CAGameType.CUSTOM;
			else
				return CAGameType.NONE;
		}

		private CARule ParseCustomRule( string ruleAsText )
		{
			CAExpressionParser parser = new CAExpressionParser();
			CARule rule;

			//try
			//{
				rule = parser.parse(ruleAsText);
			//}
			//catch (Exception ex)
			//{
			//	MessageBox.Show(this, ex.Message);
			//	return null;
			//}

			return rule;

		}

		//delegate void HandleWorkerExitedCallback();

		//public void HandleWorkerExited()
		//{
		//	if ( buttonTransStop.InvokeRequired || buttonTransOnce.InvokeRequired || labelRunStatus.InvokeRequired )
		//	{
		//		HandleWorkerExitedCallback d = new HandleWorkerExitedCallback( HandleWorkerExited );
		//		this.Invoke( d, new object[] { } );

		//	}
		//	else
		//	{
		//		buttonTransStop.BackColor = runner.workerSavedBGColor;
		//		buttonTransStop.ForeColor = runner.workerSavedFGColor;

		//		buttonTransOnce.Enabled = true;
		//		//comboPenFlavor.Enabled = true;
		//		//checkBoxDrawEnabled.Enabled = true;

		//		labelRunStatus.Text = "idle";

		//		formField.glField.MakeCurrent();

		//	}

		//}


		private void EnableItemsNeedingField( bool toState = true )
		{
			panelToolsDraw.Enabled = toState;
			panelToolsTransformations.Enabled = toState;
			panelToolsStudy.Enabled = toState;
		}

		private void EnableItemsThatResetField( bool toState = true )
		{
			if ( toState == false )
				checkBoxDrawEnabled.Checked = false;
			panelToolsInit.Enabled = toState;
			panelToolsDraw.Enabled = toState;
		}



		#endregion



		#region Menu Event Handlers

		private void menuExit_Click( object sender, EventArgs e )
		{
			Application.Exit();
		}

		private void menuAbout_Click(object sender, EventArgs e)
		{
			FormAbout fa = new FormAbout();
			fa.ShowDialog(this);
			fa.Close();
			fa = null;
		}

		private void menuDebugWindow_Click(object sender, EventArgs e)
		{
			if ( formDebug == null )
			{
				menuDebugWindow.Checked = true;

				// determine how to display debug window
				bool needToSetPosition = true;
				if ( this.WindowState == FormWindowState.Maximized )
					needToSetPosition=false;

				formDebug = new FormDebug( this, perf );

				if ( needToSetPosition )
				{
					formDebug.Left = this.Left + this.Width;
					formDebug.Top = this.Top;
					formDebug.Height = this.Height;
				}

				formDebug.Show( this );
			}
			else
			{
				menuDebugWindow.Checked = false;

				formDebug.Close();
				formDebug = null;
			}


			CADebugger.DebugPrintLine( CADebugger.DebugMessageSource.Default, 
				"Also please see file called '_Debug.log' in the working directory." );

		}

		private void menuDebugFieldChunks_Click( object sender, EventArgs e )
		{
			if ( formDebugChunks != null )
			{
				menuDebugFieldChunks.Checked = false;
				formDebugChunks.Close();
				formDebugChunks=null;
				return;
			}


			if ( formDebugChunks == null  )
			{
				if ( field == null )
				{
					MessageBox.Show( this, "First you need to make a field, please." );
					return;
				}

				menuDebugFieldChunks.Checked = true;

				formDebugChunks = new FormDebugChunks(this, field.fieldCM);

				if ( this.WindowState == FormWindowState.Normal )
				{
					formDebugChunks.Left = this.Left + this.Width;
					formDebugChunks.Top = this.Top;
					formDebugChunks.StartPosition = FormStartPosition.Manual;
				}
				else
					formDebugChunks.StartPosition = FormStartPosition.CenterParent;

				formDebugChunks.Show( this );
			}
		}


		private void menuDebug_Click( object sender, EventArgs e )
		{
			// make sure check marks are proper

			menuDebugWindow.Checked = ( formDebug != null );
			menuDebugFieldChunks.Checked = ( formDebugChunks != null );
		}

		private void menuSettings_Click( object sender, EventArgs e )
		{
			FormSettings formSettings = new FormSettings();
			formSettings.StartPosition = FormStartPosition.CenterParent;
			formSettings.ShowDialog( this );
		}

		private void menuRefreshField_Click( object sender, EventArgs e )
		{
			// stop transformations
			buttonTransStop_Click(null,null);

			DisplayField();
		}

		private void menuFixDisplay_Click( object sender, EventArgs e )
		{
			// stop transformations
			buttonTransStop_Click( null, null );

			this.panelFieldHolder.Controls.Remove( formField );
			formField = null;
			PrepareField();
			formField.SetField( field );
		}



		#endregion



		#region Debugging Functions

		public void DebugPrintLine( CADebugger.DebugMessageSource? src, string s )
		{
			CADebugger.DebugPrintLine( src, s);
		}

		public void ShowErrorDialog(string message)
		{
			MessageBox.Show(this, message, "ERROR");

		}

		#endregion

		private void labelRunStatus_Click( object sender, EventArgs e )
		{
			if ( runner.workerRunning )
				labelRunStatus.Text = "-->worker is running";
			else
				labelRunStatus.Text = "-->worker is NOT running";


		}





	}//class
}//ns
